@file:OptIn(ExperimentalTime::class)

package com.gitee.wsl.time.schedule.flow

import com.gitee.wsl.ext.base.currentTime
import com.gitee.wsl.time.millisecondsLong
import com.gitee.wsl.time.schedule.TimeScheduler
import kotlinx.coroutines.currentCoroutineContext
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.isActive
import kotlin.time.Instant
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.ExperimentalTime

/**
 * **This flow is [cold](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-flow/)**
 *
 * Will emit all the [KronScheduler.next] as soon as possible. In case [KronScheduler.next] return null, flow will
 * be completed
 *
 * @param since Will be used as the first parameter for [KronScheduler.next] fun. If passed null, `flow`
 * will always start since the moment of collecting start
 */
fun TimeScheduler.asFlowWithoutDelays(since: Instant? = null): Flow<Instant> = flow {
    var previous = since ?: currentTime
    while (currentCoroutineContext().isActive) {
        val next = next(previous).getOrNull() ?: break
        emit(next)
        previous = next + 1.milliseconds
    }
}

/**
 * **This flow is [cold](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-flow/)**
 *
 * This [Flow] will use [asFlowWithoutDelays], but stop on each time until this time will happen
 */
fun TimeScheduler.asFlowWithDelays(): Flow<Instant> = asFlowWithoutDelays().onEach { futureHappenTime ->
    delay((futureHappenTime - currentTime).millisecondsLong)
}